﻿namespace Test_Harness
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Pyramid;
    using Pyramid.Hardware;
    using Pyramid.Hardware.Cpu;
    using Pyramid.Hardware.Interop;
    using Pyramid.Hardware.MicroArchitecture;

    class Program
    {
        static void Main(string[] args)
        {
            int wordSize = Tryte.TritsPerTryte;

            #region Old Tests

            //var inputs = new IEnumerable<ILine>[3];

            //inputs[0] = "01Z".DecodeToTernaryLines();
            //inputs[1] = "1Z0".DecodeToTernaryLines();
            //inputs[2] = "Z01".DecodeToTernaryLines();

            //ManualInputLine selectorLine = new ManualInputLine();

            //BusMultiplexer testMultiplexer = new BusMultiplexer(selectorLine, inputs);

            //selectorLine.SetInput(Trivalent.True);

            //Console.WriteLine(testMultiplexer.OutputBus.EncodeAsBalancedTernary());

            //selectorLine.SetInput(Trivalent.Unknown);

            //Console.WriteLine(testMultiplexer.OutputBus.EncodeAsBalancedTernary());

            //selectorLine.SetInput(Trivalent.False);

            //Console.WriteLine(testMultiplexer.OutputBus.EncodeAsBalancedTernary());

            //var inputA = "11Z01Z01Z".DecodeToTernaryLines();

            //var inputB = "1Z01Z01Z0".DecodeToTernaryLines();

            //var clock = new ManualClock();

            //var sourceRegisterA = new Register(inputA, clock.Output);
            //var sourceRegisterB = new Register(inputB, clock.Output);

            //var testOperation = new AddOperation(sourceRegisterA.OutputBus, sourceRegisterB.OutputBus);

            //var destinationRegister = new Register(testOperation.OutputBus, clock.Output);

            //Console.WriteLine(destinationRegister.OutputBus.EncodeAsBalancedTernary());

            ////Tick sequence
            ////1: Source registers read from Input
            ////2: Source registers change output
            ////3: Destination register reads from test operation
            ////4: Destination register changes output

            //for (int tickCounter = 0; tickCounter < 4; tickCounter++)

            //{
            //    clock.Tick();
            //    //System.Threading.Thread.Sleep(100);

            //    Console.Write("{0}: ", tickCounter + 1);

            //    Console.Write(destinationRegister.OutputBus.EncodeAsBalancedTernary());

            //    Console.WriteLine();
            //}

            #endregion

            CricketTest(wordSize);

            Console.ReadLine();
        }

        private static void CricketTest(int wordSize)
        {
            IEnumerable<Tryte> valueList = new List<Tryte>() { 121, 40 };

            var clock = new ManualClock();

            var manualInputBus = new VariableInputBus(wordSize);

            var testCpu = new Cricket(wordSize, 8, manualInputBus.OutputBus, clock.Output);

            //Initial State
            testCpu.ControlStore.SetData(0, Tryte.Parse("0t0000000Z1"));
            //Data Request
            testCpu.ControlStore.SetData(1, Tryte.Parse("0t0001000Z1"));
            //LD A
            testCpu.ControlStore.SetData(2, Tryte.Parse("0t1000Z0ZZ1"));
            //Data Request
            testCpu.ControlStore.SetData(3, Tryte.Parse("0t0001000Z1"));
            //LD B
            testCpu.ControlStore.SetData(4, Tryte.Parse("0t1000Z00Z1"));
            //A + B => A
            testCpu.ControlStore.SetData(5, Tryte.Parse("0t1Z0000ZZ1"));
            //NOT B => A
            testCpu.ControlStore.SetData(6, Tryte.Parse("0t1Z1000ZZ1"));
            //HALT
            testCpu.ControlStore.SetData(7, Tryte.Parse("0t0000000Z0"));

            var outputBus = new OutputBus(testCpu.Accumulator);

            testCpu.DataRequest.SignalChange += new EventHandler<SignalChangeEventArgs>((sender, args) =>
            {
                if (((ILine)sender).Signal == Trivalent.True)
                {
                    manualInputBus.SetValue(valueList.First());

                    valueList = valueList.Skip(1);
                }
            });

            for (int clockCycle = 0; clockCycle < 9; clockCycle++)
            {
                clock.Cycle();

                Console.WriteLine();
                Console.WriteLine("Accumulator Value: {0}", outputBus.Value.ToTryte());
                DisplayRegisters(testCpu.RegisterFile);
            }
        }

        private static void DisplayRegisters(RegisterFile registerFile)
        {
            int registerIndex = 0;

            Console.WriteLine();

            foreach (var registerOutput in registerFile.OutputBuses)
            {
                Console.WriteLine("{0}: {1}", ++registerIndex, new OutputBus(registerOutput).Value.ToTryte());
            }
        }
    }
}
